package achilles.com.wuziqi;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

import java.util.ArrayList;

/**
 * TODO: document your custom view class.
 */
public class FiveStonePlane extends View {

    private float mPlaneWidth;
    private float mItemWidth;
    private final int MAX_LINE = 15;
    private Paint mPaint = new Paint();
    private Bitmap mBlackPiece;
    private Bitmap mWhitePiece;

    private final float ratioPieceOfItemWidth = (float) (3.0 / 4.0);
    private ArrayList<Point> mListWhitePoint = new ArrayList<>();
    private ArrayList<Point> mListBlackPoint = new ArrayList<>();
    private boolean mIsBlack = true;

    private final String INSTENCE = "INSTENCE";
    private final String KEY_WHITE_PIECE_LIST = "KEY_WHITE_PIECE_LIST";
    private final String KEY_BLACK_PIECE_LIST = "KEY_BLACK_PIECE_LIST";

    private boolean mIsGameOver;
    private boolean mIsBlackWinner;
    private boolean mIsWhiteWinner;

    private final int MAX_COUNT_IN_LINE = 5;

    public FiveStonePlane(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        mPaint.setColor(getResources().getColor(R.color.lineColor));
        mPaint.setAntiAlias(true);
        mPaint.setDither(true);
        mPaint.setStyle(Paint.Style.STROKE);

        mWhitePiece = BitmapFactory.decodeResource(getResources(), R.drawable.white_stone);
        mBlackPiece = BitmapFactory.decodeResource(getResources(), R.drawable.black_stone);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);

        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int targetSize = Math.min(widthSize, heightSize);

        if (widthMode == MeasureSpec.UNSPECIFIED) {
            targetSize = heightSize;
        } else if (heightMode == MeasureSpec.UNSPECIFIED) {
            targetSize = widthSize;
        }
        setMeasuredDimension(targetSize, targetSize);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mIsGameOver) return false;
        if (event.getAction() == MotionEvent.ACTION_DOWN)
            return true;
        if (event.getAction() == MotionEvent.ACTION_UP) {
            int x = (int) ((int) event.getX() / mItemWidth);
            int y = (int) ((int) event.getY() / mItemWidth);
            Point point = new Point(x, y);

            if (mListWhitePoint.contains(point) || mListBlackPoint.contains(point))
                return false;

            if (mIsBlack) {
                mListBlackPoint.add(point);
            } else {
                mListWhitePoint.add(point);
            }

            invalidate();
            mIsBlack = !mIsBlack;
            return true;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        mPlaneWidth = w;
        mItemWidth = (float) (mPlaneWidth * 1.0 / MAX_LINE);

        int picWidthHeight = (int) (mItemWidth * ratioPieceOfItemWidth);
        mWhitePiece = Bitmap.createScaledBitmap(mWhitePiece, picWidthHeight, picWidthHeight, false);
        mBlackPiece = Bitmap.createScaledBitmap(mBlackPiece, picWidthHeight, picWidthHeight, false);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBoard(canvas);
        drawPiece(canvas);
        checkGameOver();
    }

    private void checkGameOver() {
        mIsWhiteWinner = checkFiveInLine(mListWhitePoint);
        mIsBlackWinner = checkFiveInLine(mListBlackPoint);

        if (mIsWhiteWinner || mIsBlackWinner) {
            mIsGameOver = true;
            String text = mIsWhiteWinner ? "白棋胜利" : "黑棋胜利";
            Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show();
        }
    }

    private boolean checkFiveInLine(ArrayList<Point> pointArrayList) {

        for (Point point : pointArrayList) {
            int x = point.x;
            int y = point.y;
            boolean win = checkHorizontal(x, y, pointArrayList);
            if (win) return true;
            win = checkVertical(x, y, pointArrayList);
            if (win) return true;
            win = checkLeftDiagonal(x, y, pointArrayList);
            if (win) return true;
            win = checkRightDiagonal(x, y, pointArrayList);
            if (win) return true;
        }
        return false;
    }

    private boolean checkHorizontal(int x, int y, ArrayList<Point> pointArrayList) {
        int count = 1;
        for (int i = 0; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x - i, y))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;

        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x + i, y))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;
        return false;
    }

    private boolean checkVertical(int x, int y, ArrayList<Point> pointArrayList) {
        int count = 1;
        for (int i = 0; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x, y - i))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;

        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x, y + i))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;
        return false;
    }

    private boolean checkLeftDiagonal(int x, int y, ArrayList<Point> pointArrayList) {
        int count = 1;
        for (int i = 0; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x - i, y + i))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;

        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x + i, y - i))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;
        return false;
    }

    private boolean checkRightDiagonal(int x, int y, ArrayList<Point> pointArrayList) {
        int count = 1;
        for (int i = 0; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x - i, y - i))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;

        for (int i = 1; i < MAX_COUNT_IN_LINE; i++) {
            if (pointArrayList.contains(new Point(x + i, y + i))) {
                count++;
            } else {
                count = 1;
                break;
            }
        }
        if (count == MAX_COUNT_IN_LINE) return true;
        return false;
    }

    private void drawPiece(Canvas canvas) {
        for (Point point : mListBlackPoint) {
            float x = point.x * mItemWidth + (1 - ratioPieceOfItemWidth) / 2 * mItemWidth;
            float y = point.y * mItemWidth + (1 - ratioPieceOfItemWidth) / 2 * mItemWidth;

            canvas.drawBitmap(mBlackPiece, x, y, null);
        }
        for (Point point : mListWhitePoint) {
            float x = point.x * mItemWidth + (1 - ratioPieceOfItemWidth) / 2 * mItemWidth;
            float y = point.y * mItemWidth + (1 - ratioPieceOfItemWidth) / 2 * mItemWidth;

            canvas.drawBitmap(mWhitePiece, x, y, null);
        }
    }

    private void drawBoard(Canvas canvas) {
        for (int i = 0; i < MAX_LINE; i++) {
            float widthStartX = (float) (0.5 * mItemWidth);
            float heightStartY = (float) (0.5 * mItemWidth + i * mItemWidth);

            float widthEndX = (float) (mPlaneWidth - 0.5 * mItemWidth);
            float heightEndY = heightStartY;

            canvas.drawLine(widthStartX, heightStartY, widthEndX, heightEndY, mPaint);
            canvas.drawLine(heightStartY, widthStartX, heightEndY, widthEndX, mPaint);
        }
    }


    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            mListWhitePoint = bundle.getParcelableArrayList(KEY_WHITE_PIECE_LIST);
            mListBlackPoint = bundle.getParcelableArrayList(KEY_BLACK_PIECE_LIST);
            super.onRestoreInstanceState(bundle.getParcelable(INSTENCE));
            return;
        }
        super.onRestoreInstanceState(state);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        bundle.putParcelable(INSTENCE, super.onSaveInstanceState());
        bundle.putParcelableArrayList(KEY_BLACK_PIECE_LIST, mListBlackPoint);
        bundle.putParcelableArrayList(KEY_WHITE_PIECE_LIST, mListWhitePoint);

        return bundle;
    }

    public void reStart() {
        mListBlackPoint.clear();
        mListWhitePoint.clear();
        mIsBlackWinner = mIsWhiteWinner = false;
        mIsGameOver = false;
        invalidate();
    }
}
